iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 5
8
Modern Web

一步一腳印的React旅程系列 第 5

[筆記][React]Component的狀態State及生命週期Lifecycle

  • 分享至 

  • xImage
  •  

HI!上一篇我們提到了可以用Props可以從外部傳進值,就像以下做法,今天進入正文的也太突然了XD

//建立一個NowTime組件類別顯示目前時間
class NowTime extends React.Component {
    render(){
        //讀取props中取出time的值
        return <h1>現在時間是{this.props.time}</h1>
    }
}

//使用該類別做出一個實體,並將目前的時間傳入time中
ReactDOM.render(<NowTime time={new Date().toLocaleTimeString()} />
                ,document.getElementById('root'))

如果是上面的用法,雖然使用時會具有彈性,但是每次只要使用該組件,就必須在要再把值傳入time中,反過來想既然那個組件只顯示時間,為何不將time直接設定在組件當中,這樣不僅在使用時可以更方便,也能夠減少資料錯誤的機會,對吧!為了完成這件事我們要介紹組件的第二位成員State

state屬性

state屬性叫做狀態,也就是在組件中自身不會被修改的屬性,在class中可以通過constructor來設定state,例如:

class NowTime extends React.Component {
    //使用類別中的constructor建構子,參數中傳入props是必要的
    constructor(props){
        //super呼叫上一層類別也就是React.Component內的props屬性
        super(props)
        /*設定該類別的屬性,this就是指定到使用NowTime建構出來的物件
        這裡針對那個物件建立一個state的物件屬性,並在裡面設定該類別的值*/
        this.state = {time : new Date().toLocaleTimeString()}
    }
    
    render(){
        //使用this(呼叫該建構器的物件)中的state(剛剛建立的物件屬性)內的time
        return <h1>現在時間是{this.state.time}</h1>
    }
}

//直接使用該類別做出一個實體,不再另外設定傳入值
ReactDOM.render(<NowTime />,document.getElementById('root'))

https://ithelp.ithome.com.tw/upload/images/20180922/20106935xIvGdiF8jX.png
會顯示當組件被建立的時間。

這麼一來雖然在建構一個組件類別的時候需要打的程式比較多,但是在使用時就能直接呼叫類別,不必再每次呼叫類別的時候都重新設定Props,所以如果有固定不變的東西,不妨就放進去裡面吧!

組件的開始以及結束

這是文章中第二個要提到的部分:生命週期,在這個生命週期中,有兩個函式分別在建構完組件和組件結束(被移除)的時候執行,以下來認識他們吧:

componentDidMount()

class類別裡設定componentDidMount()方法的話,會在該class建構完組件的時候被執行,上方的例子使用了setInterval不斷進行重新建構,再放入畫面上的方式來刷新時間,而上方學習到的state屬性,讓我們知道可以在組件內部設定自有的資料,所以我們就可以利用改變state的值來讓畫面刷新,就不必在class外的地方操作組件的行為:

class NowTime extends React.Component {
    constructor(props){
        super(props)
        this.state={time : new Date().toLocaleTimeString()}
    }
    
    //加入組件建構完成後執行的事件
    componentDidMount(){
        /*在建構完成後,每秒都去刷新this.state.time的值
        (1)先去宣告一個更新state內容的function
        (2)每秒去執行一次該function刷新*/
        const upTime = () =>{
            //這裡面的setState()能夠重新設定state的值
            this.setState({time : new Date().toLocaleTimeString()})
        }
        setInterval(upTime,1000)
    }
    
    render(){
        return <h1>現在時間是{this.state.time}</h1>
    }
}

ReactDOM.render(<NowTime />,document.getElementById('root'))

GitHub程式碼連結
GitPage連結
透過上面調用componentDidMount()的方式,讓刷新畫面這個動作得以在組件內設置完成。不過上面是拿componentDidMount()來設定刷新頁面的function,其實只要是想在組件產生後先執行的動作,通通都可以寫在componentDidMount()裡面,所以記得哦!這是組件建構完成後接著會執行的地方。

setState()修改狀態值

再接著介紹下一位之前先認識一位新朋友,相信在上方的範例程式碼中,大家都有看到一個新的函式setState(),在組件中可以利用該函式來修改組件的state,就像上方做的那樣,不過他有幾個點是需要注意的:

  1. 只能使用setState()來修改狀態值:
    setState({time : new Date().toLocaleTimeString()})
    
    以下方法是錯誤且無效的:
    this.state.time = new Date().toLocaleTimeString()
    
  2. setState()並不是用覆蓋的方式,而是更新:
    例如組件中的state有兩個值:
    this.state = ({name:'GQSM',age:25})
    
    當我只更新nameage是不會變的,所以不必每次都重新給一堆資料
    setState({name:'GQSM_Ver2.0'})
    
  3. stateprops中的資料更新是異步的,所以當setState需要同步props的資料時,必須傳入props做使用,並回傳計算完後的結果物件給setState
    //傳入目前的state及props進行同步處理
    setState((state,props)=>({age : state.age + props.year}))
    
    以下可能會抓到錯的props資料
    setState({age : this.state.age + this.props.year})
    

以上三點中,最後一個也許會比較難理解,雖然目前只是用理論闡述需要注意這件事情,但是當之後的日子中有提到的話,會再提一次這個部分,讓大家也能夠在實作中明白這點,啊如果有大大了解的話再麻煩留言提點我,我會再把這裡補充的清楚一些!謝謝!

componentDidUpdate()

緊接著出現的是也還不是結束時執行的函式XD,上方我們學會利用了setState()修改state的值,而componentDidUpdate()正是在state被修改時會執行的函式,算是生命週期中的過程!那我們來看看他如何使用!

class NowTime extends React.Component {
    constructor(props){
        super(props)
        this.state={time : new Date().toLocaleTimeString()}
    }
    
    componentDidMount(){
        const upTime = () =>{
            this.setState({time : new Date().toLocaleTimeString()})
        }
        setInterval(upTime,1000)
    }
    
    //加入state被修改時會執行的函式
    componentDidUpdate(){
        //執行內容
        console.log('時間一分一秒在跑...')
    }
    
    render(){
        return <h1>現在時間是{this.state.time}</h1>
    }
}

ReactDOM.render(<NowTime />,document.getElementById('root'))

state中的值被修改就會執行:
https://ithelp.ithome.com.tw/upload/images/20180924/20106935Zc48CxDE9x.png

GitHub程式碼連結
GitPage連結

componentWillUnmount()

既然有開始,那就會有結束的時候,當組件被移除需要執行事情的時候就放心交給componentWillUnmount()吧!在這裡我們先簡單的在componentWillUnmount()中記錄一下被組件移除的時間點:

class NowTime extends React.Component{
    constructor(props){
        super(props)
        this.state = {time : new Date().toLocaleTimeString()}
    }
    
    componentDidMount(){
        const upTime = () =>{
            this.setState({time : new Date().toLocaleTimeString()})
        }
        setInterval(upTime,1000)
    }
    //組件結束時會執行的事件
    componentWillUnmount(){
        //這裡記錄移除掉的時間
        console.log(`移除組件的時間為:${this.state.time}`)
    }
    
    render(){
        return <h1>現在時間是:{this.state.time}</h1>
    }
}

ReactDOM.render(<NowTime />,document.getElementById('root'))

好的,上方已經設定好移除組件時該執行的動作,可是我們該怎麼移除掉組件呢?沒問題!想到操作組件就會想到ReactDOM,只要是控制組件就交給他吧!

ReactDOM中有個方法unmountComponentAtNode(Element)能夠移除在Element中的組件,移除完後會回傳true,但是如果移除失敗或是指定的Element中沒有組件,那就會回傳false,以下把它加進去做測試,看組件消失時會不會執行componentWillUnmount()

class NowTime extends React.Component{
    constructor(props){
        super(props)
        this.state = {time : new Date().toLocaleTimeString()}
    }
    
    componentDidMount(){
        const upTime = () =>{
            this.setState({time : new Date().toLocaleTimeString()})
        }
        setInterval(upTime,1000)
    }
    
    componentWillUnmount(){
        console.log(`移除組件的時間為:${this.state.time}`)
    }
    
    render(){
        return <h1>現在時間是:{this.state.time}</h1>
    }
}

ReactDOM.render(<NowTime />,document.getElementById('root'))

//宣告一個function,來移除document.getElementById('root')中的組件
const removeComponent = () =>{
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}

//延遲五秒後執行移除
setTimeout(removeComponent,5000)

執行結果上方的英字串是我亂打的
https://ithelp.ithome.com.tw/upload/images/20180923/20106935Crx4b9GbQb.png

5秒後消失並記錄時間:
https://ithelp.ithome.com.tw/upload/images/20180923/20106935JVu8ltiPHe.png

GitHub程式碼連結
GitPage連結

經過本編文章介紹的componentDidMount()componentWillUnmount()可以暸解到上方NowTime組件的生命週期大概是這樣子的:

  1. constructor中初始化組件內部的資料。
  2. 使用render()在網頁上輸出組件內容。
  3. 輸出後會執行componentDidMount()進行一次調用。
  4. 當組件內部的state值被修改時執行componentDidUpdate()
  5. 當組件被移除時會執行componentWillUnmount()的內容一次。

當然,每次去呼叫的組件都是獨一無二的,所以會有自己的生命週期在,不會因為他是從同一個class中建構出來而共用一個生命週期。


這是鐵人賽的第五篇,感覺還沒有找到應該有的節奏/images/emoticon/emoticon70.gif,其實我應該更放寬心去面對鐵人賽的,對吧!哈哈!

那今天的文章到這裡,感謝各位大大的觀看!如果文章中有任何問題,或是沒有解釋清楚的地方,還麻煩留言告訴我,謝謝大家/images/emoticon/emoticon41.gif

參考文章:

  1. https://reactjs.org/docs/state-and-lifecycle.html

上一篇
[筆記][React]關於Components的那件小事
下一篇
[筆記][React]進入Component的事件處理篇!
系列文
一步一腳印的React旅程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
Yvonne
iT邦新手 5 級 ‧ 2018-10-11 18:26:41

加油!一起學習!!/images/emoticon/emoticon08.gif
我覺得自己越寫越艱困了 /images/emoticon/emoticon20.gif

神Q超人 iT邦研究生 5 級 ‧ 2018-10-11 20:42:58 檢舉

加油啊!同是React好夥伴,
遇到問題可以互相討論XD
一起衝到最後/images/emoticon/emoticon69.gif

我要留言

立即登入留言